/*
    0 - jiandao
    1 - shitou
    2 - bu
*/

address Sender {
module GameIndex {
    use Std::vector;
    use Std::signer;
    use Std::debug;
    use Std::option;

    const GAMEINFO_ALREAY_EXISTS: u64 = 100;
    const GAMEINFO_NOT_EXISTS: u64 = 101;
    const GAMEINFO_FULL_ERROR: u64 = 102;
    const GAMEINFO_OPT_ERROR: u64 = 103;
    const PARAMETER_ERROR: u64 = 104;
    // ding yi wan jia xin xi
    struct UserInfo has key, drop, store, copy{
        opt: u8,
        round: u8,
        win_num: u8,
        lose_num: u8,
        addr: address
    }

    struct Game has key, drop, store {
        round: u64,
        addr: vector<address>,
        isFinished: bool,
        win_addr: option::Option<address>,
    }

    public fun init(s: &signer) {
        let addr = signer::address_of(s);
        assert!(addr == @Sender, 120);
        assert!(!exists<Game>(addr), 130);

        let game = Game {
            round: 0,
            addr: vector::empty<address>(),
            isFinished: false,
            win_addr: option::none()
        };

        move_to<Game>(s, game);

    }

    public entry fun init_counter(account: signer){
        Self::init(&account)
    }

    // join game
    public fun join_game(s: &signer) acquires Game{
        let (round, addres, _) = Self::get_gameinfo(s);
        debug::print(&round);
        assert!(round < 2, 166);

        let addr = signer::address_of(s);
        assert!(!exists<UserInfo>(addr), GAMEINFO_ALREAY_EXISTS);

        let userinfo = UserInfo {
            opt: 0,
            round: 0,
            win_num: 0,
            lose_num: 0,
            addr
        };

        move_to<UserInfo>(s, userinfo);
        set_game_info(addr);
        // (userinfo.round, userinfo.win_num, userinfo.lose_num)
    }

    public entry fun entry_join_game(account: signer) acquires Game{
       Self::join_game(&account);

    }

    public fun get_gameinfo(account: &signer): (u64, vector<address>, bool) acquires Game {

        let game = borrow_global<Game>(@Sender);

        (game.round, game.addr, game.isFinished)
    }

    public entry fun entry_get_gameinfo(account: signer): (u64, vector<address>, bool) acquires Game {
        let (round, addr, isFinished) = Self::get_gameinfo(&account);
        (round, addr, isFinished)
    }

    public fun test_get_gameinfo(account: &signer): u64 acquires Game {

        let game = borrow_global<Game>(@Sender);

        game.round
    }

    public entry fun test_entry_get_gameinfo(account: signer): u64 acquires Game {
        let round = Self::test_get_gameinfo(&account);
        round
    }

    public fun test_entry_get_gameinfo1(account: signer): u64 acquires Game {
        let game = borrow_global<Game>(@Sender);
        game.round
    }

    public entry fun test_entry_get_gameinfo2(account: signer): u8 acquires UserInfo {
        // let game = borrow_global<Game>(@Sender);
        // game.round
        let user_opt = borrow_global<UserInfo>(signer::address_of(&account)).opt;
        user_opt
    }

    public entry fun test_entry_get_gameinfo3(account: signer): u8 acquires UserInfo {
        // let game = borrow_global<Game>(@Sender);
        // game.round
        let user_opt = borrow_global<UserInfo>(signer::address_of(&account)).opt;
        user_opt
    }

    public fun get_message(addr: address): u8 acquires UserInfo {
        *&borrow_global<UserInfo>(addr).opt
    }

    public fun set_game_info(userinfo_address: address) acquires Game {

        let game = borrow_global_mut<Game>(@Sender);

        game.round = game.round + 1;

        vector::push_back(&mut game.addr, userinfo_address);

    }

    public fun game_start(s :&signer, _opt: u8) acquires Game, UserInfo {

        let addr = signer::address_of(s);

        assert!(exists<UserInfo>(addr), 170);
        let (_, game_addr, isFinished) = Self::get_gameinfo(s);
        assert!(!isFinished, 199);
        let (flag, index) = vector::index_of<address>(&game_addr, &addr);

        let opponent: u64;
        if (index < 1) { opponent = 1; } else { opponent = 0; };
        let myself_addr = vector::borrow<address>(&game_addr, index);
        let opponent_address = vector::borrow<address>(&game_addr, opponent);

        let (opponent_round, opponent_opt) = get_opponent_info(*opponent_address);
        let myself_info = borrow_global_mut<UserInfo>(*myself_addr);

        assert!(myself_info.round <= opponent_round, 199);
        myself_info.round = myself_info.round + 1;
        myself_info.opt = _opt;

        if (myself_info.round == opponent_round) {
            let winner = game_winner_math(myself_info.opt, opponent_opt);
            if (winner == 1) {
                set_game_end_results(*myself_addr);

            } else if (winner == 2) {
                set_game_end_results(*opponent_address);
            }
        }
    }

    public entry fun entry_game_start(s:signer, opt: u8) acquires Game, UserInfo {
        Self::game_start(&s,opt);
    }

    public fun get_opponent_info(opp_addr: address): (u8, u8) acquires UserInfo {
        let opponent_list = borrow_global<UserInfo>(opp_addr);

        (opponent_list.round, opponent_list.opt)
    }



    public fun set_game_end_results(win_addr: address) acquires Game {
        let game = borrow_global_mut<Game>(@Sender);
        game.isFinished = true;
        game.win_addr = option::some(win_addr);
    }

    public fun get_game_end_results(account: signer): (option::Option<address>, u8, u8, u64) acquires Game, UserInfo{

        let (_, game_addr, isFinished) = Self::get_gameinfo(&account);
        assert!(isFinished, 199);

        let winner = borrow_global<Game>(@Sender);

        let index1_address =  vector::borrow<address>(&game_addr, 0);
        let index2_address=  vector::borrow<address>(&game_addr, 1);

        let index1_info = borrow_global<UserInfo>(*index1_address);
        let index2_info = borrow_global<UserInfo>(*index2_address);

        (winner.win_addr, index1_info.opt, index2_info.opt, winner.round)
    }


    // you xi suan fa
    public fun game_winner_math(opt_a: u8, opt_b: u8): u8 {
        assert!(opt_a <= 2, PARAMETER_ERROR);
        assert!(opt_b <= 2, PARAMETER_ERROR);

        if (opt_a == opt_b) {
            return 0
        } else if(opt_a > opt_b) {
            let o = opt_a - opt_b;
            return o
        } else {
            if (opt_b - opt_a == 2) {
                return 1
            } else if(opt_b - opt_a == 1) {
                return 2
            } else {
                return 0
            }
        };
        return 0
    }

}
}
